
-- Matias Blanco & Sergio Perez
-- Pintores. Citas en ADA

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with Ada.Numerics.Float_Random; use Ada.Numerics.Float_Random;

procedure PintoresCitasAda is

   G: Generator; -- Para los random

   -- Numero maximo de pinturas
   MAX_BOTES: constant Integer:= 4;
   -- .. de pintores
   MAX_PINTORES: constant Integer:= 8;
   -- .. y de cuadros
   MAX_CUADROS: constant Integer:= 5;
   -- y el maximo tiempo en pintar
   MAX_TPINTA: constant Float:= 12.0;
   -- y el que menos...
   MIN_TPINTA: constant Float:= 2.0;
   -- numero de botes de cada color
   N_BOTES_COL : constant Integer:= 2;

   -- Necesitamos saber que botes necesitaremos por cuadro y
   -- los botes que hay para cada color.
   type TCuadroBotes is array (1..MAX_BOTES) of Boolean;
   type TBotesHay is array (1..MAX_BOTES) of Integer;

   -- Los pintores esperando y los botes que esperan...
   type TPintoresEspera is array (1..MAX_PINTORES) of Boolean;
   type TBotesEspera is array (1..MAX_PINTORES) of TCuadroBotes;


   -- Hay que pintar MAX_CUADROS
   type TCuadros is array (1..MAX_CUADROS) of TCuadroBotes;


   -- Un pintor
   task type Pintor is
      entry Init(Idd: Integer);
      entry Coge_Botes;
   end Pintor;

   Pintores: array (1..MAX_PINTORES) of Pintor;

   -- La Sociedad de pintores
   task Sociedad is
      entry Dame_Botes(Ok: out Boolean; Id: Integer; Botes: TCuadroBotes);
      entry Devuelve_Botes(Id: Integer; Botes: TCuadroBotes);
   end Sociedad;


   ------------------------------------
   -- Procedimiento para repartir botes
   procedure Repartir_Botes(PintoresEspera: in out TPintoresEspera; BotesEspera: in out TBotesEspera; Botes_Disponibles: in out TBotesHay) is
      Suficientes: Boolean := True;
   begin
      -- Repartimos (sin prioridad)... podria fastidiar
      -- el pintor que va antes siempre...
      -- Por eso al terminar de pintar y devolver
      -- los botes, esperan.
      for I in 1..MAX_PINTORES loop
         Suficientes := True;
         if (PintoresEspera(I)) then
            for J in 1..MAX_BOTES loop
               if (BotesEspera(I)(J) and Botes_Disponibles(J)=0) then
                  Suficientes := False;
               end if;
            end loop;

            -- Si hay, se los damos a este pintor I
            if (Suficientes) then
               for J in 1..MAX_BOTES loop
                  if (BotesEspera(I)(J)) then
                     Botes_Disponibles(J) := Botes_Disponibles(J) - 1;
                  end if;
               end loop;
               Put("Le repartimos los botes al pintor numero" & Integer'Image(I));New_Line;
               PintoresEspera(I) := False; -- Ya no espera mas.
               -- Y se los damos, que estaba esperando despistado
               Pintores(I).Coge_Botes;
            end if;
         end if;
      end loop;
   end Repartir_Botes;
   --------------------------------------



   -- El cuerpo del pintor
   task body Pintor is
      Id: Integer;
      Sus_Cuadros: TCuadros; -- Cuadros que pinta
      Oki: Boolean;    -- Le dan los botes al pobre?
      NumeroAleatorio: Float;

   begin
      accept Init (Idd: Integer) do
         Id:= Idd;
         -- Vamos a simular que necesita o no
         -- ciertas pinturas para cada cuadro
         for I in 1..MAX_CUADROS loop
            for J in 1..MAX_BOTES loop
               NumeroAleatorio:= Random(G);
               -- 50% de tener o no ese color
               if (NumeroAleatorio < 0.5) then
                  Sus_Cuadros(I)(J):= True;
               else
                  Sus_Cuadros(I)(J):= False;
               end if;
            end loop;
         end loop;
      end Init;

      -- Ale, a pintar!
      for I in 1..MAX_CUADROS loop
         Put("El pintor numero " & Integer'Image(Id) & " empieza el cuadro numero " & Integer'Image(I));New_Line;
         -- Pedimos los colores
         Sociedad.Dame_Botes(OKI, Id, Sus_Cuadros(I));
         -- Si no se los dan en el momento, los esperamos
         if (not OKI) then
            accept Coge_Botes;
         end if;
         -- Tarda un tiempo aleatorio en pintarlo
         NumeroAleatorio:= Random(G);
         delay Duration( ((MAX_TPINTA-MIN_TPINTA) * NumeroAleatorio) + MIN_TPINTA );
         --
         Put("El pintor numero " & Integer'Image(Id) & " termina el cuadro numero " & Integer'Image(I));New_Line;
         Sociedad.Devuelve_Botes(Id, Sus_Cuadros(I));
         -- Que descanse un poco ...
         delay 5.0;
      end loop;
      Put("El pintor numero " & Integer'Image(Id) & " ha terminado TODOS sus cuadros.");New_Line;
   end Pintor;


   -- El cuerpo de la sociedad.
   task body Sociedad is
      Botes_Disponibles: TBotesHay; -- En stock
      PintoresEspera : TPintoresEspera; -- Los pintores que esperan botes
      BotesEspera: TBotesEspera;

   begin
      -- No espera ningun pintor en el comienzo
      for I in 1..MAX_PINTORES loop
         PintoresEspera(I):= False;
      end loop;
      -- Y tenemos todos los botes en stock
      for I in 1..MAX_BOTES loop
         Botes_Disponibles(I):= N_BOTES_COL;
      end loop;

      loop
         select
            accept  Dame_Botes(Ok: out Boolean; Id: Integer; Botes: TCuadroBotes) do
               Put("El pintor numero " & Integer'Image(Id) & " ha pedido botes...");New_Line;
               OK := True;
               for I in 1..MAX_BOTES loop
                  if (Botes(I) and Botes_Disponibles(I)=0) then
                     OK:= False;
                  end if;
               end loop;

               if (not OK) then
                  PintoresEspera(Id):=True;
                  BotesEspera(Id):= Botes;
                  Put("No hay botes en este momentos para el pintor numero " & Integer'Image(Id));New_Line;
               else
                  -- Se las damos
                  for I in 1..MAX_BOTES loop
                     if (Botes(I)) then
                        Botes_Disponibles(I) := Botes_Disponibles(I) - 1;
                     end if;
                  end loop;
                  Put("Le damos los botes al pintor numero " & Integer'Image(Id));New_Line;
               end if;
            end Dame_Botes;
         or
            accept Devuelve_Botes(Id: Integer; Botes: TCuadroBotes) do
               for I in 1..MAX_BOTES loop
                  if (Botes(I)) then
                     Botes_Disponibles(I) := Botes_Disponibles(I) + 1;
                  end if;
               end loop;
               Put("El pintor numero " & Integer'Image(Id) & " ha dejado sus botes");New_Line;
               -- Tenemos colores ... quiza no todos
               -- pero vamos a repartir si podemos
               Repartir_Botes(PintoresEspera, BotesEspera, Botes_Disponibles);
            end Devuelve_Botes;
         end select;
      end loop;
   end Sociedad;


begin
   Put("Pintores - Matias Blanco & Sergio Perez");New_Line;New_LIne;
   Reset(G);

   -- Lanzamos los pintores
   for I in 1..MAX_PINTORES loop
      Pintores(I).Init(I);
      delay 1.0; -- optativo.
   end loop;
end PintoresCitasAda;


